#pragma once

#include "buffer.h"
#include "obj_pointer.h"
#if __x86_64__
#include "../arch/x86.h"
#else
#endif


OPEN_JLIB_NS


template<bool _BigEndian = true>
struct BitBuffer : public Obj {

private:
  typedef Ptr<BitBuffer> pointer_t;

  inline BitBuffer (Ptr<Buffer>& buffer, uwl  r_index, uwl w_index, u8 r_cache, u8 w_cache) noexcept
  : _buffer(buffer)
  , _r_index(r_index)
  , _w_index(w_index)
  , _r_cache(r_cache)
  , _w_cache(w_cache) {
  }


public:
  inline explicit BitBuffer (uwl capacity = 0) noexcept : _buffer(capacity), _r_index(0), _w_index(0), _r_cache(0), _w_cache(0) {
  }

  inline explicit BitBuffer (Ptr<Buffer>& buffer) noexcept : _buffer(buffer), _r_index(0), _w_index(0), _r_cache(0), _w_cache(0) {
  }

  inline Ptr<BitBuffer> duplicate () noexcept {
    return pointer_t::create(_buffer, _r_index, _w_index, _r_cache, _w_cache);
  }

  inline uwl readable () noexcept {
    return _w_index - _r_index;
  }

  inline uwl writable () noexcept {
    return _buffer->size() - _w_index;
  }

  inline uwl readIndex () noexcept {
    return _r_index;
  }

  inline uwl writeIndex () noexcept {
    return _w_index;
  }


#if __x86_64__
  inline u64 read (uwl bits) noexcept {
    debug_assert(_buffer && bits <= sizeof(u64) * 8 && readable() >= bits);
    u64 result = 0;
    auto index = _r_index & 0x7;
    if (index) {
      u64 lowBits = 8 - index;
      if (bits < lowBits) {
        _r_index += bits;
        return x86_bextr<u64>((u64)_r_cache, (lowBits - bits) | (bits << 8));
      }
      result = x86_bextr<u64>((u64)_r_cache, lowBits << 8);
      _r_index += lowBits;
      bits -= lowBits;
    }
    for (; bits >= 8; bits -= 8) {
      result <<= 8;
      result |= _r_cache = _buffer->read(_r_index >> 3);
      _r_index += 8;
    }
    if (bits) {
      result <<= 8;
      _r_cache = _buffer->read(_r_index >> 3);
      _r_index += bits;
      result |= x86_bextr<u64>((u64)_r_cache, (8 - bits) | (bits << 8));
    }
    return result;
  }
#else
#endif


private:
  Ptr<Buffer> _buffer;
  uwl _r_index;
  uwl _w_index;
  u8 _r_cache;
  u8 _w_cache;
};


CLOSE_JLIB_NS